<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .box{width:400px;height:100px;border:solid 1px black;margin: 50px auto;display: flex;align-items: center;text-align:center;}
        .box span{width:40px;}
        .box em{width:40px;font-style: normal;}
        .box .bar{width:280px;position: relative;}
        .bar p{margin:0;height:30px;width:0px;background: #55f;}
        .bar p i{position: absolute;left:0px;width:20px;height:50px;top:-10px;background: #00f;border-radius: 6px;cursor: pointer;}
    </style>
</head>
<body>
    <div class="box">
        <span>0%</span>
        <em>0</em>
        <div class="bar">
            <p><i></i></p>
        </div>
        <em>100</em>
    </div>
</body>
<script>
    const oi = document.querySelector(".box .bar i");
    const op = document.querySelector(".box .bar p");
    const ospan = document.querySelector(".box span");
    const obar = document.querySelector(".box .bar");

    // 提前计算最大拖拽的left，以备后用
    const maxLeft = obar.offsetWidth - oi.offsetWidth;

    oi.onmousedown = function(eve){
        // 获取按下的事件对象，以备将来获取按下的一瞬间相对于元素的坐标
        const downE = eve || window.event;
        downE.preventDefault();

        document.onmousemove = function(eve){
            // 获取移动的事件对象，以备获取相对于页面的坐标
            const moveE = eve || window.event;
            // 真实的left：相对于页面的坐标 - 按下时相对于元素的坐标 - 包含块相对于页面的偏移值
            let l = moveE.pageX - downE.offsetX - obar.offsetLeft;
            // 边界限定
            if(l<0) l=0;
            if(l>maxLeft) l = maxLeft;
            // 设置给滑块
            oi.style.left = l + "px";
            // 设置给条
            op.style.width = l + "px";
            // 计算百分比并设置
            ospan.innerHTML = parseInt((l / maxLeft) * 100) + "%";
        }
        
        document.onmouseup = function(){
            // 抬起时，清除移动和抬起事件
            document.onmousemove = document.onmouseup = null;
        }
    }
</script>
</html>